<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    * { margin: 0; padding: 0; }
    li { list-style: none; }
    #div1 { width: 800px; height: 600px; overflow: hidden; background: black; margin: 20px auto; position: relative; }
    #gameBtn { color: white; font-size: 20px; cursor: pointer; border: 1px white solid; width: 100px; height: 30px;
    line-height: 30px; text-align: center; position: absolute; top: 285px; left: 350px; }
    #score { color: #FFF; font-size: 20px }

    #bee { position: relative; width: 400px; }
    .enemy1 { width: 38px; height: 28px; background: green; float: left; border: 1px solid white; }
    .enemy2 { width: 38px; height: 28px; background: yellow; float: left; border: 1px solid white; }
    .enemy3 { width: 38px; height: 28px; background: red; float: left; border: 1px solid white; }
    
    .air1 { width: 46px; height: 60px; background: #cccccc; position: absolute; }
    .bullet { width: 1px; height: 10px; overflow: hidden; background: white; position: absolute; }
  </style>
</head>
<body>
  <div id="div1">
    <div id="gameBtn">开始游戏</div>
  </div>
  <script>
    /*
    敌人和飞机暂时都是用色块代替

    元素的生成 crateElement

    敌人的移动 setInterval

    碰撞检测 方法

    敌人跟随 算法

    关卡设计 （实现多关操作）

    其他功能（积分 血量）
    */
    window.onload = function() {
      var oBtn = document.querySelector('#gameBtn');
      oBtn.onclick = function() {
        this.style.display = 'none';

        Game.init('div1');  // 游戏开始

      }
    };


    var Game = {
      // 敌人数据
      oEnemy: {
        e1 : { style: 'enemy1', blood: 1, speed: 5, score: 1 },
        e2 : { style: 'enemy2', blood: 2, speed: 7, score: 2 },
        e3 : { style: 'enemy3', blood: 3, speed: 10, score: 3 }
      },

      // 关卡数据
      gk: [
        {
          eMap: [
            'e2','e2','e2','e2','e2','e2','e2','e2','e2','e2',
            'e2','e2','e2','e2','e2','e2','e2','e2','e2','e2',
            'e2','e2','e2','e2','e2','e2','e2','e2','e2','e2',
            'e1','e1','e1','e1','e1','e1','e1','e1','e1','e1',
            'e1','e1','e1','e1','e1','e1','e1','e1','e1','e1',
            'e1','e1','e1','e1','e1','e1','e1','e1','e1','e1',
          ],
          colNum: 10,
          iSpeedX: 10,
          iSpeedY: 10,
          times: 2000
        },
        {
          eMap: [
            'e3','e3','e3','e3','e3','e3','e3','e3','e3','e3',
            'e2','e2','e2','e2','e2','e2','e2','e2','e2','e2',
            'e2','e2','e2','e2','e2','e2','e2','e2','e2','e2',
            'e1','e1','e1','e1','e1','e1','e1','e1','e1','e1',
            'e1','e1','e1','e1','e1','e1','e1','e1','e1','e1',
            'e1','e1','e1','e1','e1','e1','e1','e1','e1','e1',
          ],
          colNum: 10,
          iSpeedX: 10,
          iSpeedY: 10,
          times: 2000
        },

      ],

      // 飞机的数据
      air: {
        style: 'air1',
        bulletStyle: 'bullet'
      },

      // 关卡
      level: 0,

      init: function( id ) { // 初始化
        // 创建场景父元素
        this.oParent = document.getElementById( id );

        this.createScore();

        this.createEnemy( this.level );  // 从第一关开始

        this.createAir();

      },

      createScore: function() {   //积分的创建
        var oS = document.createElement('div');
        oS.id = 'score';
        oS.innerHTML = '积分:<span>0</span>';
        this.oParent.appendChild(oS);

        this.oSNum = oS.getElementsByTagName('span')[0];
      },

      createEnemy: function( level ) {    // 生成关卡
        document.title = '第' + level + '关';

        // 创建下一关时,先清理上一关的遗留
        if (this.oUI) {
          clearInterval( this.oUI.timer );
          clearInterval( this.oUI.timer2 );
          this.oParent.removeChild( this.oUI );
        }

        var gk = this.gk[ level ];
        var arr = [];
        var oUI = document.createElement('ul');
        this.oParent.appendChild( oUI );
        // 在添加到节点树后

        oUI.id = 'bee';
        oUI.style.width = gk.colNum * 40 + 'px';
        
        oUI.style.left = (this.oParent.offsetWidth - oUI.offsetWidth) / 2 + 'px';

        this.oUI = oUI; // 定义为全局属性，方便在其他方法调用

        for (var i = 0; i < gk.eMap.length; i++) {
          var oLi = document.createElement('li');
          oLi.className = this.oEnemy[ gk.eMap[i] ].style;
          oLi.blood = this.oEnemy[ gk.eMap[i] ].blood;
          oLi.speed = this.oEnemy[ gk.eMap[i] ].speed;
          oLi.score = this.oEnemy[ gk.eMap[i] ].score;

          oUI.appendChild( oLi );
        }

        this.aLi = oUI.getElementsByTagName('li');

        // 初始 ul中的li为float,需要转换成绝对定位
        for (var m = 0; m < this.aLi.length; m++) {
          arr.push( [this.aLi[m].offsetLeft, this.aLi[m].offsetTop] )
        }

        for (var j = 0; j < this.aLi.length; j++) {
          this.aLi[j].style.position = 'absolute';
          this.aLi[j].style.left = arr[j][0] + 'px';
          this.aLi[j].style.top = arr[j][1] + 'px';
        }
 
        this.runEnemy();
      },

      runEnemy: function() {  // 移动敌人
        var gk = this.gk[ this.level ];
        var This = this;

        var L = 0;
        var R = this.oParent.offsetWidth - this.oUI.offsetWidth;

        This.oUI.timer = setInterval(function() {
          if (This.oUI.offsetLeft > R) {
            gk.iSpeedX *= -1;
            This.oUI.style.left = This.oUI.offsetLeft + gk.iSpeedX + 'px';
          } else if (This.oUI.offsetLeft < L) {
            gk.iSpeedX *= -1;
            This.oUI.style.left = This.oUI.offsetLeft + gk.iSpeedX + 'px';
          }
          
          This.oUI.style.top = This.oUI.offsetTop + gk.iSpeedY + 'px';
        }, 200);

        This.oUI.timer2 = setInterval(function(){

          This.oneMove();

        }, gk.times)

      },

      oneMove: function() {   // 单兵作战
        var curLi = this.aLi[ Math.floor(Math.random() * this.aLi.length)];
        var self = this;
        curLi.timer = setInterval(function() {
          var a = (self.oA.offsetLeft + self.oA.offsetWidth / 2) - (curLi.offsetLeft + curLi.parentNode.offsetLeft + curLi.offsetWidth / 2);
          var b = (self.oA.offsetTop + self.oA.offsetHeight / 2) - (curLi.offsetTop + curLi.parentNode.offsetTop + curLi.offsetHeight / 2);
          var c = Math.sqrt( a * a + b * b);

          var iSx = curLi.speed * a / c;
          var iSy = curLi.speed * b / c;

          curLi.style.left = curLi.offsetLeft + iSx + 'px';
          curLi.style.top = curLi.offsetTop + iSy + 'px';

          if (self.pz( self.oA, curLi )) {
            alert('游戏结束');
            window.location.reload();
          }

        }, 30);
      },

      createAir: function() {   // 创建飞机
        var oA = document.createElement('div');
        this.oA = oA;
        oA.className = this.air.style;
        this.oParent.appendChild(oA);
        // 先添加到节点树，再定位
        oA.style.left = (this.oParent.offsetWidth - oA.offsetWidth) / 2 + 'px';
        oA.style.top = this.oParent.offsetHeight - oA.offsetHeight + 'px';

        this.bindAir();
      },

      bindAir: function() {     // 添加对飞机的控制

        var timer = null;
        var iNum = 0;
        var This = this;
        var speed = 10;

        document.onkeydown = function(ev) {
          var ev = ev || window.event;
          if (!timer) {
            timer = setInterval(show, 30);
          }
          if (ev.keyCode == 37) {         // 左方向键
            iNum = 1;
          } else if (ev.keyCode == 39) {  // 右方向键
            iNum = 2;
          }
        };

        document.onkeyup = function(ev) {
          var ev = ev || window.event;
          clearInterval(timer);
          timer = null;
          iNum = 0;

          if (ev.keyCode == 32) {   // 空格键
            This.createBullet();
          }
        };

        function show() {
          if (iNum === 1) {
            speed = -10;
            This.oA.offsetLeft > 0 && (This.oA.style.left = This.oA.offsetLeft + speed + 'px');
          } else if (iNum === 2) {
            speed = 10;
            This.oA.offsetLeft < (This.oParent.offsetWidth - This.oA.offsetWidth) && (This.oA.style.left = This.oA.offsetLeft + speed + 'px');
          }

          
        }

      },

      createBullet: function() {    //创建子弹
        var oB = document.createElement('div');
        oB.className = this.air.bulletStyle;
        this.oParent.appendChild(oB);
        oB.style.left = this.oA.offsetLeft + (this.oA.offsetWidth - oB.offsetWidth) / 2 + 'px';
        oB.style.top = this.oA.offsetTop - oB.offsetHeight + 'px';

        this.runBullet( oB );
      },

      runBullet: function( oB ) {   // 子弹的运动
        var This = this;
        oB.timer = setInterval(function() {
          if (oB.offsetTop < -oB.offsetHeight) {
            clearInterval( oB.timer );
            This.oParent.removeChild( oB );
          } else {
            oB.style.top = oB.offsetTop - 10 + 'px';
            for (var i = 0; i < This.aLi.length; i++) {
              if (This.pz(oB, This.aLi[i])) {

                if (This.aLi[i].blood == 1) {
                  // 删掉定时器
                  clearInterval( This.aLi[i].timer );
                  // 更新积分
                  This.oSNum.innerHTML = parseInt(This.oSNum.innerHTML, 10) + This.aLi[i].score;
                  This.oUI.removeChild( This.aLi[i] );
                } else {
                  This.aLi[i].blood--;
                }
                
                clearInterval( oB.timer );    // 注意在删除元素前，需要先删除定时器
                This.oParent.removeChild( oB );
              }
            }
          }

          // 开启下一关
          if ( !This.aLi.length ) {
            This.createEnemy(++This.level);
          }

        }, 30);
      },


      pz: function(obj1, obj2) {  // 碰撞检测
        // obj1 的左右上下4边
        var L1 = obj1.offsetLeft;
        var R1 = obj1.offsetLeft + obj1.offsetWidth;
        var T1 = obj1.offsetTop;
        var B1 = obj1.offsetTop + obj1.offsetHeight;

        // obj2 的左右上下4边
        var L2 = obj2.offsetLeft + obj2.parentNode.offsetLeft;
        var R2 = obj2.offsetLeft + obj2.parentNode.offsetLeft + obj2.offsetWidth;
        var T2 = obj2.offsetTop + obj2.parentNode.offsetTop;
        var B2 = obj2.offsetTop + obj2.parentNode.offsetTop + obj2.offsetHeight;

        if (R1 < L2 || L1 > R2 || B1 < T2 || T1 > B2) {   // 没有碰撞的情况
          return false;
        } else {
          return true;
        }
      }
    }
  </script>
</body>
</html>